package com.quanquan.user.service.autoCompute.service;

import DTO.PageDTO;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.quanquan.user.configBeans.RabbitSender;
import com.quanquan.user.entity.ConsumptionAmount;
import com.quanquan.user.entity.User;
import com.quanquan.user.service.autoCompute.JavaSqlUtil;
import com.quanquan.user.service.autoCompute.constant.SourceTableConst;
import com.quanquan.user.service.autoCompute.constant.SymbolConst;
import com.quanquan.user.service.autoCompute.constant.SynConst;
import com.quanquan.user.service.autoCompute.constant.TypeConst;
import com.quanquan.user.service.autoCompute.dto.TableField;
import com.quanquan.user.service.autoCompute.entity.MemberLabel;
import com.quanquan.user.service.autoCompute.enums.OptionType;
import com.quanquan.user.service.autoCompute.repository.MemberLabelRepository;
import com.quanquan.user.service.autoCompute.request.MemberLabelRequestBody;
import exceptions.service.ServiceRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import utils.DateUtils;
import utils.StringUtil;
import utils.UUIDGenerator;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.Predicate;
import javax.transaction.Transactional;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author gongzhou
 * @title: MemberLabelService
 * @projectName zt
 * @description: TODO
 * @date 2019/6/229:22
 */
@Slf4j
@Service
public class MemberLabelService {

    @Autowired
    private RabbitSender rabbitSender;

    @Autowired
    private MemberLabelRepository memberLabelRepository;

    @Autowired
    private GeneralService generalService;

    @PersistenceContext
    private EntityManager entityManager;


    public List<TableField> getFields(String type) {
        Class<?> clazz = null;
        switch (type) {
            case SourceTableConst.MEMBER:
                clazz = User.class;
                break;
            case SourceTableConst.CONSUME:
                clazz = ConsumptionAmount.class;
                break;
            case SourceTableConst.PARK:
                break;
            case SourceTableConst.INTEGRAL:
                break;
            default:
                clazz = null;
        }
        if (null == clazz) {
            return null;
        }
        return generalService.getTableFields(clazz);
    }


    /**
     * 创建标签
     *
     * @param requestBody
     * @return
     * @throws ServiceRuntimeException
     */
    @Transactional
    public MemberLabel create(MemberLabelRequestBody requestBody) throws ServiceRuntimeException {

        String labelRule = this.convert(requestBody);

        String labelFrom = requestBody.getLabelFrom();

        if (StringUtils.isEmpty(labelFrom)) {
            throw new ServiceRuntimeException("标签来源不能为空");
        }

        if (StringUtils.isEmpty(labelRule)) {
            throw new ServiceRuntimeException("标签规则生成失败");
        }

        MemberLabel label = new MemberLabel();

        BeanUtil.copyProperties(requestBody, label, CopyOptions.create().setIgnoreNullValue(true));

        label.setId(UUIDGenerator.getUUIDforMySql());

        label.setLabelRule(labelRule);

        label.setCreateTime(new Date());

        label.setUpdateTime(new Date());

        label.setIsRemoved(false);

        label.setSynStatus(SynConst.IN_PROGRESS);

        MemberLabel save = memberLabelRepository.save(label);

        if (null == save) {
            throw new ServiceRuntimeException("保存标签失败");
        }

        return save;

    }

    public List<MemberLabel> batchSave(List<MemberLabelRequestBody> requestBodies) throws ServiceRuntimeException {
        if (requestBodies == null || requestBodies.isEmpty()) {
            throw new ServiceRuntimeException("请选择标签");
        }
        List<MemberLabel> labels = new LinkedList<>();
        requestBodies.forEach(requestBody -> {
            MemberLabel label = new MemberLabel();
            String labelRule = this.convert(requestBody);
            BeanUtil.copyProperties(requestBody, label, CopyOptions.create().setIgnoreNullValue(true));
            if (StringUtils.isEmpty(requestBody.getId())) {
                label.setId(UUIDGenerator.getUUIDforMySql());
            }
            label.setLabelRule(labelRule);
            label.setUpdateTime(new Date());
            label.setCreateTime(new Date());
            label.setIsRemoved(false);
            label.setSynStatus(SynConst.IN_PROGRESS);
            labels.add(label);
        });
        List<MemberLabel> memberLabels = (List<MemberLabel>) memberLabelRepository.saveAll(labels);
        return memberLabels;
    }

    public Long countAll(List<MemberLabelRequestBody> requestBodies) throws ServiceRuntimeException {
        if (requestBodies == null || requestBodies.isEmpty()) {
            throw new ServiceRuntimeException("请选择标签");
        }
        requestBodies = requestBodies.stream().filter(l -> StringUtil.isNotEmpty(l.getOption())).collect(Collectors.toList());
        Map<String, List<MemberLabel>> map = new LinkedHashMap<>();
        requestBodies.forEach(requestBody -> {
            convert(requestBody);
            String labelFrom = requestBody.getLabelFrom();
            MemberLabel label = new MemberLabel();
            BeanUtil.copyProperties(requestBody, label, CopyOptions.create().setIgnoreNullValue(true));
            if (map.containsKey(labelFrom)) {
                List<MemberLabel> labelList = map.get(labelFrom);
                labelList = null != labelList ? labelList : new LinkedList<>();
                labelList.add(label);
            } else {
                List<MemberLabel> labelList = new LinkedList<>();
                labelList.add(label);
                map.put(labelFrom, labelList);
            }
        });

        String countSql = JavaSqlUtil.assembleCountSql(map);
        log.info(countSql);
        Query queryCount = entityManager.createNativeQuery(countSql);

        BigInteger bi = (BigInteger) queryCount.getSingleResult();

        return bi.longValue();
    }

    /**
     * 分页获取结果
     *
     * @param requestBody
     * @return
     */
    public PageDTO<MemberLabel> list(MemberLabelRequestBody requestBody) {
        if (StringUtils.isEmpty(requestBody.getBelonging())) {
            // TODO 判断画像归属者
        }
        Sort sort = new Sort(Sort.Direction.DESC, "containsNum");

        Pageable pageable = PageRequest.of(requestBody.getPage(), requestBody.getSize(), sort);

        Page<MemberLabel> page = memberLabelRepository.findAll((Specification<MemberLabel>) (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            // 创建具体查询条件
            if (StringUtils.isNotEmpty(requestBody.getKeyword())) {
                Predicate p1 = criteriaBuilder.like(root.get("labelName").as(String.class), "%" + requestBody.getKeyword() + "%");
                Predicate p2 = criteriaBuilder.or(criteriaBuilder.like(root.get("labelDesc").as(String.class), "%" + requestBody.getKeyword() + "%"), p1);
                predicates.add(p2);
            }
            predicates.add(criteriaBuilder.equal(root.get("isRemoved").as(Boolean.class), false));
//            predicates.add(criteriaBuilder.equal(root.get("belongings").as(String.class), requestBody.getBelonging()));
            return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
        }, pageable);

        PageDTO<MemberLabel> pageResult = new PageDTO<>();

        pageResult.setPage(requestBody.getPage());

        pageResult.setPageSize(requestBody.getSize());

        pageResult.setKeyword(requestBody.getKeyword());

        pageResult.setTotal(page.getTotalElements());

        pageResult.setList(page.getContent());

        pageResult.setTotalPage(page.getTotalPages());

        return pageResult;

    }

    public MemberLabel getById(String id) throws ServiceRuntimeException {
        if (StringUtils.isEmpty(id)) {
            throw new ServiceRuntimeException("标签Id不能为空");
        }
        MemberLabel label = memberLabelRepository.findById(id).orElse(null);
        if (label == null) {
            throw new ServiceRuntimeException("未发现该标签");
        }
        return label;
    }

    /**
     * 根据ID集合获取标签
     *
     * @return
     */
    public List<MemberLabel> getByIds(List<String> ids) throws ServiceRuntimeException {
        if (null == ids || ids.isEmpty()) {
            throw new ServiceRuntimeException("id集合不能为空");
        }
        return memberLabelRepository.findByIdIn(ids);
    }

    public Map<String, List<MemberLabel>> list2Map(List<MemberLabel> labels) {
        Map<String, List<MemberLabel>> map = new LinkedHashMap<>();
        if (labels == null || labels.isEmpty()) {
            return map;
        }
        labels.forEach(label -> {
            String labelFrom = label.getLabelFrom();
            if (map.containsKey(labelFrom)) {
                List<MemberLabel> labelList = map.get(labelFrom);
                labelList = null != labelList ? labelList : new LinkedList<>();
                labelList.add(label);
            } else {
                List<MemberLabel> labelList = new LinkedList<>();
                labelList.add(label);
                map.put(labelFrom, labelList);
            }
        });
        map.forEach((table, labelList) -> {
            List<MemberLabel> distinct = distinct(map.get(table));
            map.replace(table, map.get(table), distinct);
        });
        return map;
    }

    private List<MemberLabel> distinct(List<MemberLabel> origin) {
        return origin.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getLabelRule()))), ArrayList::new));
    }

    @Transactional
    public MemberLabel update(MemberLabelRequestBody requestBody) throws ServiceRuntimeException {

        String sql = this.convert(requestBody);

        String id = requestBody.getId();

        if (StringUtils.isEmpty(id)) {
            throw new ServiceRuntimeException("标签id不能为空");
        }

        MemberLabel label = memberLabelRepository.findById(id).orElse(null);

        if (null == label) {
            throw new ServiceRuntimeException("该标签不存在");
        }

        label.setLabelRule(sql);

        BeanUtil.copyProperties(requestBody, label, CopyOptions.create().setIgnoreNullValue(true));

        label.setUpdateTime(new Date());

        MemberLabel save = memberLabelRepository.save(label);

        return save;
    }

    public MemberLabel save(MemberLabel label) {
        return memberLabelRepository.save(label);
    }

    @Transactional
    public void remove(String id) throws ServiceRuntimeException {
        MemberLabel label = this.getById(id);
        label.setIsRemoved(true);
        memberLabelRepository.save(label);
    }

    public void removeAll(List<String> ids) throws ServiceRuntimeException {
        if (ids == null || ids.isEmpty()) {
            throw new ServiceRuntimeException("Id不能为空");
        }
        memberLabelRepository.deleteAllByIdIn(ids);
    }

    /**
     * 根据用户Id查询标签
     *
     * @param customerId
     * @return
     */
    public List<MemberLabel> list(String customerId) {
        return memberLabelRepository.findAllByIsRemovedFalse();
    }

    /**
     * 同步用户标签
     *
     * @param labelId
     */
    public void syncLabel(String labelId) {
        Map<String, Object> properties = new HashMap<>();
        properties.put("memberLabelId", labelId);
        String exchange = "memberLabel.exchange";
        String routingKey = "memberLabel.sync";
        rabbitSender.sendMessageToMQ("同步用户标签", properties, exchange, routingKey);
    }

    /**
     * 检测标签符号和值的合法性并运算
     *
     * @param condition
     * @return
     * @throws ServiceRuntimeException
     */
    public String convert(MemberLabelRequestBody condition) throws ServiceRuntimeException {
        if (condition == null) throw new ServiceRuntimeException("筛选条件不能为空");
        if (StringUtil.isEmpty(condition.getTableName())) throw new ServiceRuntimeException("表名不能为空");
        String option = condition.getOption();
        OptionType optionType = condition.getOptionType();
        condition.setUseTmp(false);
        if (OptionType.BOOL.equals(optionType)) {
            condition.setFieldType(TypeConst.BOOL);
            if (SymbolConst.FALSE.equals(option)) {
                condition.setRightValue(SymbolConst.NO);
            } else if (SymbolConst.TRUE.equals(option)) {
                condition.setRightValue(SymbolConst.YES);
            }
            condition.setRightSymbol(SymbolConst.EQ);
        } else if (OptionType.POINT.equals(optionType)) {
            condition.setFieldType(TypeConst.INT);
            condition.setRightSymbol(SymbolConst.EQ);
            condition.setRightValue(option);
        } else if (OptionType.RANGE.equals(optionType)) {
            String[] r = option.split(SymbolConst.SUB);
            condition.setLeftValue(r[0]);
            try {
                condition.setRightValue(r[1]);
            } catch (Exception e) {
            }

            condition.setRightSymbol(SymbolConst.L_E);
            condition.setLeftSymbol(SymbolConst.G_E);
        } else if (OptionType.FUZZY.equals(optionType)) {
            condition.setFieldType(TypeConst.STR);
            condition.setRightSymbol(SymbolConst.LIKE);
            condition.setRightValue(option);
        } else if (OptionType.SPAN.equals(optionType)) {
            if (!SymbolConst.isDateRange(option)) {
                throw new ServiceRuntimeException("请选择正确的日期范围");
            }
            condition.setFieldType(TypeConst.DATE);
        } else if (OptionType.FREQ.equals(optionType)) {
            String[] r = option.split(SymbolConst.SUB);
            condition.setLeftValue(r[0]);
            condition.setRightValue(r[1]);
            condition.setLeftSymbol(SymbolConst.G_E);
            condition.setRightSymbol(SymbolConst.L_E);
            String fieldName = condition.getFieldName();
            String tableName = condition.getTableName();
            condition.setUseTmp(true);
//            condition.setTempTable(SQLHandler.assembleTmpTable(fieldName, tableName));
            condition.setTempName(StringUtil.getRandomStringByLength(6));
        } else {
            throw new ServiceRuntimeException("不支持的操作类型 " + optionType);
        }
        String field, type, rightSymbol = condition.getRightSymbol(), rightValue = condition.getRightValue(),
                leftSymbol = condition.getLeftSymbol(), leftValue = condition.getLeftValue();
        if (StringUtils.isEmpty(field = condition.getFieldName())) throw new ServiceRuntimeException("字段不能为空");
        if (StringUtils.isEmpty(type = condition.getFieldType())) throw new ServiceRuntimeException("字段类型不能为空");
        if (TypeConst.BOOL.equals(type)) {                                              //判断布尔类型
            if (!SymbolConst.EQ.equals(rightSymbol)) throw new ServiceRuntimeException("判断只做相等处理");
            else if (!SymbolConst.YES.equals(rightValue) && !SymbolConst.NO.equals(rightValue))
                throw new ServiceRuntimeException("请选择‘是’或‘否’");
            else return JavaSqlUtil.assembleSingle(field, rightSymbol, rightValue);
        } else if (TypeConst.DATE.equals(type)) {//判断时间类型
            if (StringUtils.isNotEmpty(leftValue) && StringUtils.isNotEmpty(rightValue)) {
                Date forward = DateUtils.parseStr2Date(DateUtils.TIMESTAMP, leftValue);
                Date after = DateUtils.parseStr2Date(DateUtils.TIMESTAMP, rightValue);
                if (forward.after(after)) throw new ServiceRuntimeException("开始时间不能在结束时间之后");
                if (forward.equals(after)) throw new ServiceRuntimeException("开始时间不能等于结束时间");
                return JavaSqlUtil.assembleDate(field, SymbolConst.AND, leftValue, leftSymbol, rightValue, rightSymbol);
            } else if (StringUtils.isEmpty(leftValue) && StringUtils.isEmpty(rightValue)) {
                return JavaSqlUtil.assembleDateRange(field, condition.getOption());
            } else {
                return JavaSqlUtil.assembleDate(field, null, leftValue, leftSymbol, null, null);
            }
        } else if (TypeConst.INT.equals(type) || TypeConst.BDC.equals(type)) {          //判断数字类型
            if (StringUtils.isNotEmpty(leftValue) && StringUtils.isNotEmpty(rightValue)) {
                if (!StringUtils.isNumeric(leftValue)) throw new ServiceRuntimeException("请输入数字类型左值");
                if (!StringUtils.isNumeric(rightValue)) throw new ServiceRuntimeException("请输入数字类型右值");
                if (Integer.valueOf(leftValue) > Integer.valueOf(rightValue))
                    throw new ServiceRuntimeException("最大值不能小于最小值");
                return JavaSqlUtil.assembleStrNum(field, SymbolConst.AND, leftValue, leftSymbol, rightValue, rightSymbol);
            } else if (StringUtils.isNotEmpty(leftValue) && StringUtils.isEmpty(rightValue)) {
                return JavaSqlUtil.assembleSingleStrNum(field, leftSymbol, leftValue);
            } else {
                return JavaSqlUtil.assembleSingleStrNum(field, rightSymbol, rightValue);
            }
        } else if (TypeConst.STR.equals(type)) {                                        //判断String类型
            if (SymbolConst.LIKE.equals(rightSymbol)) {
                return JavaSqlUtil.assembleSingleStrNum(field, rightSymbol, rightValue);
            } else {
                return JavaSqlUtil.assembleStrNum(field, SymbolConst.AND, leftValue, leftSymbol, rightValue, rightSymbol);
            }
        } else {
            throw new ServiceRuntimeException("不支持该类型");
        }
    }

}
